import Router, {asyncRouterMap, frameworkRouter, WHITE_LIST, layoutWhiteRouter} from '../router'
// import { USER_TOKEN } from '@/utils/constant'
import {setLocalStorageItem} from '@/utils/storage-helper'
import {ORGANIZATION_TREE} from '@/utils/constant'
import {login} from '@/services/model/login'
import {getMenuTree} from '@/services/model/user'
import {getMonitorInfo} from '@/services/model/project-info'
import {getOrganizationTree} from '@/services/model/user'
import Store from '@/store'

const Base64 = require('js-base64').Base64

/**
 * @param {*} to 即将要进入的目标 路由对象
 * @param {*} from 当前导航正要离开的路由
 * @param {*} next 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数
 */

/**
 * @param next(): 进行管道中的下一个钩子。如果全部钩子执行完了，则导航的状态就是 confirmed (确认的)。
 * @param next(false): 中断当前的导航。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮)，那么 URL 地址会重置到 from 路由对应的地址。
 * @param next('/') 或者 next({ path: '/' }): 跳转到一个不同的地址。当前的导航被中断，然后进行一个新的导航。你可以向 next 传递任意位置对象，且允许设置诸如 replace: true、name: 'home' 之类的选项以及任何用在 router-link 的 to prop 或 router.push 中的选项。
 * @param next(error): (2.4.0+) 如果传入 next 的参数是一个 Error 实例，则导航会被终止且该错误会被传递给 router.onError() 注册过的回调。
 */

Router.beforeEach((to, form, next) => {
  // 设置文档title
  setTitle(to)
  // const TOKEN = getLocalStorageItem(USER_TOKEN)
  // TOKEN ? _hasRouter(to, form, next) : _hasInWhiteList(to.path, next)
  _hasRouter(to, form, next)
})

// 设置文档title
const setTitle = to => {
  if (to.meta?.name) {
    document.title = to.meta.name
  }
}

// token存在，则走验证流程
function _hasRouter(to, from, next) {
  let router = Store.state.addRouter
  // first: 初始addRouter为空数组，空数组则ajax拉取数据
  // second: addRouter为null时，是在 上一条中 ajax拉取数据失败或者 权限为空 时会设置为null. 则正常访问即可，会此匹配到404页面
  // three: addRouter长度大于0时，则正常访问页面
  // routeMatch 当存在用路由 /login/admin这种方式通过param传参时，通过routeMatch来匹配路由
  router?.length === 0 && !WHITE_LIST.includes(to.meta?.routeMatch || to.path) ? _getRouterMap(to, next) : _hasAccess(to.meta?.routeMatch || to.path, next)
}

// 系统中存在的路由map
const EXIT_ROUTER_MAP = asyncRouterMap.map(x => x.path)

// 验证当前访问url是否存在以及是否有权限访问
function _hasAccess(path, next) {
  // 用户拥有的权限
  const HAS_ROUTER_MAP = Store.getters.hasRouterMap
  // 是否是系统中已有的路由
  const IS_IN_EXIT = EXIT_ROUTER_MAP.includes(path)
  // 是否为用户已有的权限的路由
  const IS_NOT_IN_HAS = !HAS_ROUTER_MAP.includes(path)
  // 是否为路由白名单路径
  const IS_NOT_IN_WHITE = !WHITE_LIST.includes(path)
  IS_IN_EXIT && IS_NOT_IN_HAS && IS_NOT_IN_WHITE ? next('/403') : next()
}

// 查找并返回对应的路由item
function _findRouterItem(PATH) {
  let result = asyncRouterMap.find(y => (y.meta?.routeMatch || y.path) === PATH || y.path.includes(PATH + '?') || PATH.includes(y.path + '?'))
  return result || {}
}

// axios获取路由
async function _getRouterMap(to, next) {
  // 从接口取到DSL菜单描述
  let ROUTER_MAP = await queryMenuDSL(to, next)
  // 如果获取的权限DSL为空，则将此处的addRouter从空数组变成null
  // 具体详见_hasRouter 中的注释
  if (!ROUTER_MAP) {
    Store.commit('setState', {name: 'addRouter', value: null})
    // routeMatch 当存在用路由 /login/admin这种方式通过param传参时，通过routeMatch来匹配路由
    _hasAccess(to.meta?.routeMatch || to.path, next)
    return false
  }
  // 查询用户信息
  Store.dispatch('queryUserInfo')
  // 路由解析
  // highlight 有值：存在一二级目录，传入的值为一级目录的id。  用于该目录下的所有二级目录匹配高亮
  // highlight 无值：则代表本身就是一级目录含有连接，值为本身的id
  let _resolveRouteItem = (ITEM, routerList, highlight) => {
    // 查找路由item
    let routerItem = _findRouterItem(ITEM.url)
    // 查找出重复路由
    let repetRouter = routerList.find(k => k.path === routerItem.path)
    // 如果路由不为空对象时才能进行添加
    if (Object.keys(routerItem).length !== 0 && !repetRouter) {
      if (!routerItem.meta) routerItem.meta = {}
      // 获取当前菜单设置的权限点 用于各页面控制权限点 wrSign  1查看，  2 操作
      let authority = ITEM.wrSign
      // 如果当前路由仍有子集，且是子集path === ''的情况时，代表该子集才是路由的真实父级
      let childItem = routerItem.subMenu?.find(x => x.path === '')
      // 将动态取到的name字段设置到 路由中meta中字段，用于 nav-tag组件
      // highlight 用于匹配一级目录高亮 所以的的二级目录都存储一级目录的id作为高亮标识
      if (childItem) childItem.meta = {...childItem.meta, authority, name: ITEM.name, highlight: highlight || ITEM.id}
      else routerItem.meta = {...routerItem.meta, authority, name: ITEM.name, highlight: highlight || ITEM.id}
      routerList.push(routerItem)
    }
  }
  // 解析DSL
  let _resolveList = (MENU, menuList = [], routerList = [], highlight) => {
    MENU.map(x => {
      // 根据DSL生成菜单item
      let item = {name: x.name, path: x.url, icon: x.menuIcon, id: x.id, children: []}
      // 规定：有url时，则再有子集都是权限点.
      if (x.url) _resolveRouteItem(x, routerList, highlight)
      else item.children = _resolveList(x.subMenu || [], [], routerList, x.id).menuList
      menuList.push(item)
    })
    return {menuList, routerList}
  }
  let {menuList, routerList} = _resolveList(ROUTER_MAP)
  // 兼容当menuList为空数组时，会导致死循环
  routerList = routerList.length === 0 ? null : routerList
  Store.commit('setState', {name: 'menuList', value: menuList})
  Store.commit('setState', {name: 'addRouter', value: routerList})
  let frameworkCHild = [...routerList, ...layoutWhiteRouter]
  let homeRouteItem = frameworkCHild.find(x => (x.match || x.path) === '/home') || frameworkCHild[0]
  homeRouteItem.alias = '/'
  // 将获取到权限路由 挂载在layout布局下
  frameworkRouter.children = frameworkCHild
  // 挂载到路由实例
  Router.addRoutes([frameworkRouter])
  next({...to})
}

// 接口获取用户信息
// eslint-disable-next-line no-unused-vars
// 这里的返回值需特殊注意： 当无路由添加时，需返回null。 不能返回空数组
// 详见_hasRouter方法中的判断
async function queryMenuDSL(to, next) {

  try {
    // path: `/project/${row.projectId}/work-point/${row.workPointId}/monitor/${row.monitorType}?activeId=${row.pointId}`
    //路基系统单点登录，自动登录 http://www.jgbpy.com:9999/web/NT3dee/z0TBW3boRjyYl4YHTX7Het?userName=&token=
    if (to.path === '/NT3dee/z0TBW3boRjyYl4YHTX7Het') {
      let token = to.query.token
      let userName = to.query.userName
      await _handleLogin(userName, token, next)
      return
    }
    //http://localhost:8080/passToken/monitor?token=eyJ1c2VybmFtZSI6InRva2VuUGFzcyIsInBhc3N3b3JkIjoiZXlKMWMyVnlibUZ0WlNJNkluIn0=&monitor=1624963638448275458-1624967951031648257-1624968043826429954-8
    if (to.path === '/passToken/monitor') {
      let token = to.query.token
      let monitorId = to.query.monitor
      let list=JSON.parse(Base64.decode(token))
      let userName = list.username
      let password = list.password
      let data = Base64.encode(userName + password)
      await _handleLogin(userName, data, next, monitorId)
      return

    }
    let {data} = await getMenuTree()
    // 将获取到的DSL存到store.供其他地方使用
    Store.commit('setState', {name: 'nodeList', value: data || []})
    return data?.length === 0 ? null : data
  } catch (error) {
    return null
  }
}

// 处理单点登录
async function _handleLogin(userName, token, next, monitorId) {
  //通过organizationId 实现登录接口
  const DATA = {
    token: token,
    loginName: userName,
  }
  try {
    await login(DATA)
    let {data} = await getMenuTree()
    let {data2} = await getOrganizationTree()
    //缓存一天过期
    setLocalStorageItem(ORGANIZATION_TREE, data2, 1)
    Store.commit('setState', {name: 'nodeList', value: data || []})
    if (!monitorId) {
      next('/road-mgmt')
    } else {
      let {data}=await getMonitorInfo({id:monitorId})
      //外部跳转过来
      next(`/project/${data.projectId}/work-point/${data.workPointId}/monitor/${data.monitorType}?activeId=${data.id}`)
    }

  } catch (error) {
    next('/403')
  }
}

